package com.byd.sealmanage.shiro;

import com.byd.sealmanage.modal.bo.LayuiMenu;
import com.byd.sealmanage.modal.po.TbRole;
import com.byd.sealmanage.modal.po.TbToken;
import com.byd.sealmanage.modal.po.TbUser;
import com.byd.sealmanage.service.LoginService;
import jdk.nashorn.internal.parser.Token;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.session.Session;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.util.ByteSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;

import java.util.Collection;
import java.util.HashSet;
import java.util.Optional;
import java.util.Set;

/**
 * 认证领域
 *
 * @author ls
 * @version 2.5.1
 * @since 2019-01-10
 */
@Slf4j
@Configuration
public class AuthRealm extends AuthorizingRealm {

    @Autowired
    private LoginService loginService;

    @Override
    public boolean supports(AuthenticationToken token) {
        return token instanceof Oauth2Token;
    }

    /**
     * ** 执行认证逻辑**
     * 认证回调函数,登录时调用
     * 首先根据传入的用户名获取User信息；然后如果user为空，那么抛出没找到帐号异常UnknownAccountException；
     * 如果user找到但锁定了抛出锁定异常LockedAccountException；最后生成AuthenticationInfo信息，
     * 交给间接父类AuthenticatingRealm使用CredentialsMatcher进行判断密码是否匹配，
     * 如果不匹配将抛出密码错误异常IncorrectCredentialsException；
     * 另外如果密码重试次数太多将抛出超出重试次数异常ExcessiveAttemptsException；
     * 在组装SimpleAuthenticationInfo信息时， 需要传入：身份信息（用户名）、凭据（密文密码）、盐（username+salt），
     * CredentialsMatcher使用盐加密传入的明文密码和此处的密文密码进行匹配。
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        String accessToken = (String) token.getPrincipal();

        //根据accessToken，查询用户信息
        TbToken tokenEntity = loginService.selectByToken(accessToken);
        //token失效
        if (tokenEntity == null || tokenEntity.getExpireTime().getTime() < System.currentTimeMillis()) {
            throw new IncorrectCredentialsException("token失效，请重新登录");
        }

        //查询用户信息
        TbUser user = loginService.getUserByJobNumber(tokenEntity.getUserId());
        //账号锁定
        if ("2".equals(user.getStatus())){
            throw new LockedAccountException("账号已被锁定,请联系管理员");
        }

        return new SimpleAuthenticationInfo(user, accessToken, getName());
    }

    /**
     * **执行授权逻辑**
     * 只有需要验证权限时才会调用, 授权查询回调函数, 进行鉴权但缓存中无用户的授权信息时调用.在配有缓存的情况下，只加载一次.
     * 如果需要动态权限,但是又不想每次去数据库校验,可以存在ehcache中.自行完善
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principal) {
        Session session = SecurityUtils.getSubject().getSession();
        TbUser user = (TbUser) session.getAttribute("USER_SESSION");
        // 权限信息对象info,用来存放查出的用户的所有的角色（role）及权限（permission）
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
        // 用户的角色集合
        Set<String> roleIds = new HashSet<>();
        Set<String> permit = new HashSet<>();

        Set<TbRole> roles = user.getRoles();
        for(TbRole role : roles){
            roleIds.add(role.getRoleId());
            for(LayuiMenu menu : role.getMenus()){
                if(menu.getPerms() != null && !"".equals(menu.getPerms())){
                    permit.add(menu.getPerms());
                }
            }
            for (LayuiMenu buttons : role.getButton()){
                if(buttons.getPerms() != null || !"".equals(buttons.getPerms())){
                    permit.add(buttons.getPerms());
                }
            }
        }
        log.info(user.getJobnumber() + "角色集合：" + roleIds.toString() + "权限集合：" + permit.toString());
        info.setRoles(roleIds);
        Collection<String> permission = permit;
        info.addStringPermissions(permission);
        return info;
    }
}
